#include "stdafx.h"
#include "simple_log_impl.h"
#include <assert.h>
#include <windows.h>
#include "tools.h"
#include "log_thread.h"

SimpleLogImpl::SimpleLogImpl()
    : file_handle_(INVALID_HANDLE_VALUE),
      file_name_(L""),
      level_(SimpleLog::kLevelDebug) {
    Init();
}

SimpleLogImpl::~SimpleLogImpl() {
    Clean();
}

void SimpleLogImpl::Init() {
    level_name_[SimpleLog::kLevelDebug]     = std::wstring(L"Debug   ");
    level_name_[SimpleLog::kLevelInfo]      = std::wstring(L"Info    ");
    level_name_[SimpleLog::kLevelWarning]   = std::wstring(L"Warning ");
    level_name_[SimpleLog::kLevelFatal]     = std::wstring(L"Fatal   ");

    LogThread::Get()->Start();
}

void SimpleLogImpl::Clean() {
    if (file_handle_ != INVALID_HANDLE_VALUE) {
        ::CloseHandle(file_handle_);
        file_handle_ = INVALID_HANDLE_VALUE;
    }

    file_name_ = L"";
    level_ = SimpleLog::kLevelDebug;

    LogThread::Get()->Stop();
}

bool SimpleLogImpl::Create(const std::wstring& file_name, bool overwrite, SimpleLog::SIMPLE_LOG_LEVEL level) {
    HANDLE h = ::CreateFile(
        file_name.c_str(),
        GENERIC_WRITE,
        0,
        NULL,
        overwrite ? CREATE_ALWAYS : OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (h == INVALID_HANDLE_VALUE) {
        return false;
    }

    ::SetFilePointer(h, 0, NULL, FILE_END);

    file_handle_ = h;
    file_name_ = file_name;

    return true;
}

void SimpleLogImpl::SetLevel(SimpleLog::SIMPLE_LOG_LEVEL level) {
    level_ = level;
}

void SimpleLogImpl::WriteLog(SimpleLog::SIMPLE_LOG_LEVEL level, const std::wstring& log) {
    // filter log
    if (level < level_)
        return;

    if (file_handle_ != INVALID_HANDLE_VALUE) {
        SYSTEMTIME st;
        ::GetLocalTime(&st);

        wchar_t head[256] = { 0 };
        swprintf_s(head, L"%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d T:%u %s: ",
            st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
            ::GetCurrentThreadId(),
            level_name_[level].c_str());

        std::wstring* x_log = new std::wstring(head);
        x_log->append(log);
        x_log->append(L"\n"); // always write a line break


        LogThread::Get()->PostThreadMessag(
            WM_WRITELOG,
            reinterpret_cast<WPARAM>(this),
            reinterpret_cast<LPARAM>(x_log));
    }
}

void SimpleLogImpl::WriteFormatLog(SimpleLog::SIMPLE_LOG_LEVEL level, const wchar_t* format, ...)
{
    // filter log
    if (level < level_)
        return;

    if (file_handle_ != INVALID_HANDLE_VALUE) {
        va_list args;
        va_start(args, format);

        wchar_t buffer[32 * 1024] = { 0 };
        int length = _vsnwprintf_s(buffer, _countof(buffer), _TRUNCATE, format, args);

        va_end(args);

        WriteLog(level, buffer);
    }
}

void SimpleLogImpl::WriteLog(const std::wstring& log) {
    std::string mb_log = SysWideToMultiByte(log, CP_UTF8);

    DWORD write_bytes = 0;
    ::WriteFile(
        file_handle_,
        mb_log.c_str(),
        mb_log.length(),
        &write_bytes,
        NULL);
}
